require 'rubygems'
require 'blankslate' unless defined? BlankSlate
require 'active_support'
require 'json'

module Notator
#-------------
  
  class JavascriptObjectNotation < BlankSlate
  #------------------------------------------
    
    def initialize( options = {} )
    #-------------------------------
      
      @pretty = options[ :pretty ] || false;
    
      @context_stack = [];
      @context_stack.push( Hash.new )
    
    end

    def hash!( key, *args, &block )
    #------------------------------
      method_missing( key.to_sym, *args, &block )
    end
    
    alias :tag! :hash!
    
    def array!( key = nil, *args, &block )
    #-------------------------------------

      if block.nil?
        
        raise 'Thearray! method requires an array argument.' \
          unless args.first.is_a?( Array )
            
        @context_stack.last[ key ] = args.first
      
      else
        
        array = Array.new
        
        if @context_stack.last.is_a?( Hash )

          raise 'The array! method requires a key in the context of a hash.' \
            if key.nil?

          @context_stack.last[ key ] = array
          
        else  
            
          @context_stack.last.push( array );
          
        end
        
        @context_stack.push( array )
        block.call( self )
        @context_stack.pop
      
      end
    
    end

    def value!( value = nil, &block )
    #--------------------------------

      raise 'The value! method can only be called in the context of ' + 
            'an array.' \
        unless @context_stack.last.is_a?( Array )

      if block.nil?
            
        @context_stack.last.push( value )
              
      else
                    
        hash = Hash.new
        @context_stack.last.push( hash )
        @context_stack.push( hash )
        block.call( self )
        @context_stack.pop
      
      end
    
    end

    def method_missing( key, *args, &block )
    #---------------------------------------

      raise 'A key-value pair cannot be added in the context of an array.' \
        if @context_stack.last.is_a?( Array )
      
      if block.nil?
        @context_stack.last[ key ] = args.first
      else
        hash = Hash.new
        @context_stack.last[ key ] = hash
        @context_stack.push( hash )
        block.call( self )
        @context_stack.pop
      end
    
    end
    
    def <<( json )
    #-------------
      
      hash = JSON[ json ]
      
      @context_stack.last.is_a?( Array ) ?
        @context_stack.last.push( hash ) :
        @context_stack.last.merge!( hash )
                     
    end

    def to_json
    #----------
      @pretty ? 
        JSON.pretty_generate( @context_stack.first ) :
        @context_stack.first.to_json
    end

  end

end
